﻿using LogSystem;
using System.Collections.Generic;
using UnityEngine;

namespace WorldMapBase.WorldScene
{
    public class GradientManager : MonoBehaviour
    {
        public List<GradientSetting> gradientSettings;
        private Dictionary<int, GradientSetting> _gradientSettingMaps;

        private List<bool> _isActives; //存储每个队列的激活状态

        private List<float> _globalTimes; //存储每个队列的时间

        private List<bool> _showGlobalAnimation; //存储每个队列是否播放动画曲线

        private List<Material> _allMaterials; //所有材质球的备份

        private float _activeTime = 0.0f;
        private float _animationMaxTime = 0.0f; //记录所有队列中动画曲线的时长最长的那个的时间长度

        private bool _activeThisFunction = false; //当调用SetGradient()方法时，该变量将被设为true，Update()方法检测为true时将开始执行播放动画的方法，
        //此时_activeTime将开始计时，循环超过所有动画曲线的时长中最长的那个时将_activeThisFunction设为false，即停止循环

        private void Awake()
        {
            //初始化
            _isActives = new List<bool>();
            _globalTimes = new List<float>();
            _showGlobalAnimation = new List<bool>();
            _allMaterials = new List<Material>();

            _gradientSettingMaps = new Dictionary<int, GradientSetting>();
            for (int i = 0; i < gradientSettings.Count; i++)
            {
                var gradientSetting = gradientSettings[i];

                _gradientSettingMaps[i] = gradientSetting;

                //初始化shader全局变量，防止材质球直接变成透明的
                //判断每个队列是否填写globalProperties,并将状态记录在队列中，在下面的计算中会判断如果值是空着的就直接不做计算
                if (_gradientSettingMaps[i].globalAnimation.length >= 2)
                {
                    Shader.SetGlobalFloat(_gradientSettingMaps[i].globalProperties, 1);
                    _showGlobalAnimation.Add(true);

                    //比较获取所有队列哪个时间最长
                    if (_gradientSettingMaps[i].globalAnimation.keys[_gradientSettingMaps[i].globalAnimation.length - 1]
                            .time > _animationMaxTime)
                    {
                        _animationMaxTime = _gradientSettingMaps[i].globalAnimation
                            .keys[_gradientSettingMaps[i].globalAnimation.length - 1].time;
                    }
                }
                else
                {
                    _showGlobalAnimation.Add(false);
                    Debug.LogError("队列名：" + "" + _gradientSettingMaps[i].typeName + "" + "透明度动画曲线设置为空，透明渐变动画将无法生效！");
                }

                //初始化列表索引参数
                _isActives.Add(false);
                _globalTimes.Add(-0.01f);

                //初始化材质队列,将引用的材质球复制出一份
                for (int j = 0; j < _gradientSettingMaps[i].materials.Count; j++)
                {
                    var material = _gradientSettingMaps[i].materials[j];
                    if (material)
                        _allMaterials.Add(Instantiate(material));
                    else
                        Debug.LogErrorFormat(LogModule.City, $"GradientManager上材质丢失: _gradientSettingMaps[{i}].materials[{j}]");
                }
            }
        }

        private void Update()
        {
            if (_activeThisFunction)
            {
                GlobalAnimation();
                SetMaterials();

                //计算改动画循环的时长，超时将停止
                _activeTime += Time.deltaTime;
                if (_activeTime > _animationMaxTime)
                {
                    _activeThisFunction = false;
                    _activeTime = 0.0f;
                }
            }
        }

        /// <summary>
        /// int idx: 需要激活的队列索引，从0开始
        /// bool isActive: true(透明度从0到1)，false(透明度从1到0)
        /// </summary>
        /// <param name="idx"></param>
        /// <param name="isActive"></param>
        /// <returns></returns>
        public bool SetGradient(int idx, bool isActive)
        {
            bool successActive = false;
            if (_gradientSettingMaps.TryGetValue(idx, out GradientSetting gradientSetting))
            {
                gradientSetting.active = isActive;

                _isActives[idx] = isActive;

                //满足激活状态且当前层级下动画曲线不为空时
                if (isActive && _showGlobalAnimation[idx])
                {
                    //判断当前动画的时间大于第一个关键帧的时间值且小于最后一个关键字的时间值时，说明正在播放透明动画，这个时候将会继续播放而不是从第一个关键帧的时间开始
                    if (_globalTimes[idx] > 0.0f && _globalTimes[idx] < _gradientSettingMaps[idx].globalAnimation
                            .keys[_gradientSettingMaps[idx].globalAnimation.length - 1].time)
                    {
                        _globalTimes[idx] = _globalTimes[idx];
                    }
                    else
                    {
                        _globalTimes[idx] = 0.01f;
                    }

                    //Debug.Log(isActive + "---" + "当前时间" + "---" + _globalTimes[idx]);
                }
                else
                {
                    //如果当前层级没有填全局变量的值，将直接不做动画的计算
                    if (_showGlobalAnimation[idx])
                    {
                        //判断当前动画的时间大于第一个关键帧的时间值且小于最后一个关键字的时间值时，说明正在播放透明动画，这个时候将会继续播放而不是从最后一个关键帧的时间开始
                        if (_globalTimes[idx] > 0.0f && _globalTimes[idx] < _gradientSettingMaps[idx].globalAnimation
                                .keys[_gradientSettingMaps[idx].globalAnimation.length - 1].time)
                        {
                            _globalTimes[idx] = _globalTimes[idx];
                        }
                        else
                        {
                            _globalTimes[idx] = _gradientSettingMaps[idx].globalAnimation
                                .keys[_gradientSettingMaps[idx].globalAnimation.length - 1].time - 0.01f;
                        }

                        //Debug.Log(isActive + "---" + "当前时间" + "---" + _globalTimes[idx]);
                    }
                }

                successActive = true;
                _activeThisFunction = true;
                _activeTime = 0;

                string state = isActive ? "逐渐显示动画" : "逐渐隐藏动画";
                Debug.Log("渐变队列:" + " " + idx + " " + "已执行:" + " " + state);
            }

            return successActive;
        }

#if UNITY_EDITOR
        public void FindAllMaterials()
        {
            Dictionary<string, Material> materialsDictionary = new Dictionary<string, Material>();
            Awake();
            for (int i = 0; i < _isActives.Count; i++)
            {
                _gradientSettingMaps[i].materials.Clear();
                if (_gradientSettingMaps[i].gameObjects.Count != 0 && _gradientSettingMaps[i].gameObjects != null)
                {
                    for (int j = 0; j < _gradientSettingMaps[i].gameObjects.Count; j++)
                    {
                        for (int p = 0; p < _gradientSettingMaps[i].gameObjects[j].GetComponentsInChildren<Renderer>(true).Length; p++)
                        {
                            for (int k = 0; k < _gradientSettingMaps[i].gameObjects[j].GetComponentsInChildren<Renderer>(true)[p].sharedMaterials.Length; k++)
                            {
                                if (!materialsDictionary.ContainsKey(_gradientSettingMaps[i].gameObjects[j].GetComponentsInChildren<Renderer>(true)[p].sharedMaterials[k].name) && _gradientSettingMaps[i].gameObjects[j].GetComponentsInChildren<Renderer>(true)[p].sharedMaterials[k].HasProperty(_gradientSettingMaps[i].globalKeyWord))
                                {
                                    materialsDictionary.Add(_gradientSettingMaps[i].gameObjects[j].GetComponentsInChildren<Renderer>(true)[p].sharedMaterials[k].name, _gradientSettingMaps[i].gameObjects[j].GetComponentsInChildren<Renderer>(true)[p].sharedMaterials[k]);
                                    _gradientSettingMaps[i].materials.Add(materialsDictionary[_gradientSettingMaps[i].gameObjects[j].GetComponentsInChildren<Renderer>()[p].sharedMaterials[k].name]);
                                    //Debug.Log(_gradientSettingMaps[i].gameObjects[j].GetComponentsInChildren<Renderer>(true)[p].sharedMaterial.name);
                                }
                            }
                        }
                    }
                }
            }
        }

#endif

        private void GlobalAnimation()
        {
            for (int i = 0; i < _isActives.Count; i++)
            {
                //如果当前层级没有填全局变量的值，将直接不做动画的计算
                if (_showGlobalAnimation[i])
                {
                    //判断动画是否处于播放状态
                    if (_globalTimes[i] < _gradientSettingMaps[i].globalAnimation
                            .keys[_gradientSettingMaps[i].globalAnimation.length - 1].time && _globalTimes[i] > 0.0f)
                    {
                        if (_isActives[i])
                        {
                            _globalTimes[i] += Time.deltaTime;
                            //Debug.Log("状态：" + _isActives[i] + "-----" + "LOD" + i + "-----" + "当前值" + _globalTimes[i] +
                            //"" + _showGlobalAnimation[i]);
                        }
                        else
                        {
                            _globalTimes[i] -= Time.deltaTime;
                            //Debug.Log("状态：" + _isActives[i] + "-----" + "LOD" + i + "-----" + "当前值" + _globalTimes[i] +
                            //"" + _showGlobalAnimation[i]);
                        }

                        Shader.SetGlobalFloat(_gradientSettingMaps[i].globalProperties,
                            _gradientSettingMaps[i].globalAnimation.Evaluate(_globalTimes[i]));
                        //Debug.LogWarning("全局在循环！！！！！");
                    }
                }
            }
        }

        private void SetMaterials()
        {
            int materialIndex = 0;

            for (int i = 0; i < _isActives.Count; i++)
            {
                //当队列被激活且材质球数量不为空时执行
                if (_gradientSettingMaps[i].materials.Count > 0 && _gradientSettingMaps[i].materials != null)
                {
                    //判断当动画处于播放状态时，将物体设置为半透明
                    if (_globalTimes[i] < _gradientSettingMaps[i].globalAnimation
                            .keys[_gradientSettingMaps[i].globalAnimation.length - 1].time)
                    {
                        for (int j = 0; j < _gradientSettingMaps[i].materials.Count; j++)
                        {
                            _gradientSettingMaps[i].materials[j].renderQueue = 3000;
                            if (_gradientSettingMaps[i].materials[j].HasProperty("_ZWrite"))
                            {
                                _gradientSettingMaps[i].materials[j].SetInt("_ZWrite", 0);
                            }
                            if (_gradientSettingMaps[i].materials[j].HasProperty("_SrcAlpha"))
                            {
                                _gradientSettingMaps[i].materials[j].SetInt("_SrcAlpha", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
                            }
                            if (_gradientSettingMaps[i].materials[j].HasProperty("_DstAlpha"))
                            {
                                _gradientSettingMaps[i].materials[j].SetInt("_DstAlpha", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
                            }

                            _gradientSettingMaps[i].materials[j].EnableKeyword(_gradientSettingMaps[i].globalKeyWord.ToUpper() + "_ON");
                            //Debug.LogError(_gradientSettingMaps[i].materials[j].name+""+"开启变体成功");
                            materialIndex++;
                        }
                    }
                    else
                    {
                        for (int j = 0; j < _gradientSettingMaps[i].materials.Count; j++)
                        {
                            _gradientSettingMaps[i].materials[j].DisableKeyword(_gradientSettingMaps[i].globalKeyWord.ToUpper() + "_ON");
                            _gradientSettingMaps[i].materials[j].renderQueue = _allMaterials[materialIndex].renderQueue;
                            if (_gradientSettingMaps[i].materials[j].HasProperty("_ZWrite"))
                            {
                                _gradientSettingMaps[i].materials[j]
                                    .SetInt("_ZWrite", _allMaterials[materialIndex].GetInt("_ZWrite"));
                            }
                            if (_gradientSettingMaps[i].materials[j].HasProperty("_SrcAlpha"))
                            {
                                _gradientSettingMaps[i].materials[j]
                                    .SetInt("_SrcAlpha", _allMaterials[materialIndex].GetInt("_SrcAlpha"));
                            }
                            if (_gradientSettingMaps[i].materials[j].HasProperty("_DstAlpha"))
                            {
                                _gradientSettingMaps[i].materials[j]
                                    .SetInt("_DstAlpha", _allMaterials[materialIndex].GetInt("_DstAlpha"));
                            }
                            //Debug.LogError(_gradientSettingMaps[i].materials[j].name+""+"关闭变体成功");
                            materialIndex++;
                        }
                    }
                }
            }
        }

        //本地测试用
#if UNITY_EDITOR
        /*
        public void btnn(bool bl)
        {
            SetGradient(0, bl);
        }

        private void OnGUI()
        {
            GUI.Label(new Rect(250, 250, 100, 30), "Label");
            if (GUI.Button(new Rect(400,400,88,88),"Show"))
            {
                btnn(true);
            }
            
            if (GUI.Button(new Rect(300,300,88,88),"Hide"))
            {
                btnn(false);
            }
            
        }
        */
#endif

        private void OnDisable()
        {
            int materialIndex = 0;

            for (int i = 0; i < _gradientSettingMaps.Count; i++)
            {
                var gradientSetting = _gradientSettingMaps[i];
                Shader.SetGlobalFloat(gradientSetting.globalProperties, 1);
                for (int j = 0; j < gradientSetting.materials.Count; j++)
                {
                    var material = gradientSetting.materials[j];
                    if (material == null)
                    {
                        Debug.LogErrorFormat(LogModule.City, $"GradientManager上材质丢失: _gradientSettingMaps[{i}].materials[{j}]");
                        continue;
                    }

                    material.DisableKeyword(gradientSetting.globalKeyWord.ToUpper() + "_ON");

                    var rawMaterial = _allMaterials[materialIndex];
                    material.renderQueue = rawMaterial.renderQueue;
                    if (material.HasProperty("_ZWrite"))
                    {
                        material.SetInt("_ZWrite", rawMaterial.GetInt("_ZWrite"));
                    }
                    if (material.HasProperty("_SrcAlpha"))
                    {
                        material.SetInt("_SrcAlpha", rawMaterial.GetInt("_SrcAlpha"));
                    }
                    if (material.HasProperty("_DstAlpha"))
                    {
                        material.SetInt("_DstAlpha", rawMaterial.GetInt("_DstAlpha"));
                    }
                    materialIndex++;
                }

                gradientSetting.materials.Clear();
            }

            _allMaterials.Clear();
            _globalTimes.Clear();
            _isActives.Clear();
            _showGlobalAnimation.Clear();
        }
    }

}
